﻿using PMS.Core.Configuration.CommonEnumConfigs;
using PMS.Core.Utils;
using PMS.Core.Utils.Authentication;
using PMS.Data.Entities.Customers;
using PMS.Data.Entities.SystemModules;
using PMS.Services.EntityServices.Customers;
using PMS.Services.EntityServices.Stores;
using PMS.Services.EntityServices.SystemModules;
using PMS.Services.SharedKernel;
using System;
using System.Collections.Generic;
using System.Linq;

namespace PMS.Services.Utils.Security
{
    public partial class PermissionService : IPermissionService
    {
        #region Fields
        private readonly IWorkContext _workContext;
        private readonly IStoreContext _storeContext;
        private readonly IStoreMappingService _storeMappingService;
        private readonly ISystemModuleService _systemModuleService;
        #endregion

        #region Ctor

        public PermissionService(IWorkContext workContext,
            IStoreContext storeContext,
            IStoreMappingService storeMappingService,
            ISystemModuleService systemModuleService)
        {
            _workContext = workContext;
            _storeContext = storeContext;
            _storeMappingService = storeMappingService;
            _systemModuleService = systemModuleService;
        }

        #endregion

        #region Utils
        protected int DecodeId(string hashId)
        {
            if (string.IsNullOrEmpty(hashId)) return 0;
            return HashidsHelper.Decode(AuthenticationDefaultConfigs.ItemIdHashSalt, hashId);
        }
        #endregion

        #region Methods

        #region SystemModule
        /// <summary>
        /// Authorize permission of SystemModule(Navi Menu)
        /// </summary>
        /// <param name="hashId"></param>
        /// <returns></returns>
        public virtual bool AdminAuthorize(string hashId)
        {
            if(string.IsNullOrEmpty(hashId)) throw new ArgumentNullException(nameof(hashId));
            var module = _systemModuleService.GetSystemModuleById(DecodeId(hashId));
            return AdminAuthorize(_workContext.CurrentCustomer, module);
        }

        /// <summary>
        /// Authorize permission of SystemModule(Navi Menu)
        /// </summary>
        /// <param name="customer"></param>
        /// <param name="module"></param>
        /// <returns>true - authorized; otherwise, false</returns>
        public virtual bool AdminAuthorize(Customer customer, SystemModule module)
        {
            if (customer == null)
                throw new ArgumentNullException(nameof(customer));
            if( module == null)
                throw new ArgumentNullException(nameof(module));
            if (!module.IsActive || module.OpenMode == LinkOpenMode.None) return false;
            if (module.LimitedToStores)
            {
                int[] limitedStoreIds = _storeMappingService.GetStoresIdsWithAccess(module);
                if (!limitedStoreIds?.Contains(_storeContext.CurrentStore.Id) ?? false)
                    return false;
            }
            if (customer.IsAdministrator())
                return true;
            else if (customer.IsAdminUser())
            {
                var customerRoleIds = customer.GetCustomerRoles()
                                            .Where(ar => ar.RoleType == RoleType.BackendUser)
                                            .Select(ar => ar.Id);
                var moduleMappings = from mp in _systemModuleService.GetSystemModuleCustomerRoleMappingsBySystemModuleId(module.Id)
                                     where customerRoleIds.Contains(mp.CustomerRoleId)
                                     select mp;
                return moduleMappings.Any(mp => mp.ViewPermission);
            }
            else return false;
        }

        #endregion

        #region SystemModuleButton
        /// <summary>
        /// 查询分配给系统模块的所有操作按钮是否有操作权限
        /// </summary>
        /// <param name="customer"></param>
        /// <param name="module"></param>
        /// <returns>Lists of results with each assigned modulebuttonId and whether is authorized</returns>
        //public virtual IList<(int btnId, bool canAccess)> GetModuleAssignedButtonsWithAccess(Customer customer, SystemModule module)
        //{
        //    if (customer == null)
        //        throw new ArgumentNullException(nameof(customer));
        //    if (module == null)
        //        throw new ArgumentNullException(nameof(module));
        //    if (!module.IsActive || module.OpenMode == LinkOpenMode.None) return null;
        //    if (module.LimitedToStores)
        //    {
        //        int[] limitedStoreIds = _storeMappingService.GetStoresIdsWithAccess(module);
        //        if (!limitedStoreIds?.Contains(_storeContext.CurrentStore.Id) ?? false)
        //            return null;
        //    }
        //    var moduleButtonIds = module.ModuleButtonIds;
        //    if (!(moduleButtonIds?.Any() ?? false))
        //        return null;

        //    //https://stackoverflow.com/questions/3131400/why-tuples-items-are-readonly
        //    var result = new List<(int btnId, bool canAccess)>();
        //    //moduleButtonIds.ToList().ForEach(btn =>
        //    //{//set default
        //    //    result.Add((btn, false));
        //    //});
        //    if (customer.IsAdministrator())
        //    {
        //        //result.ForEach(r => r.canAccess = true);
        //        moduleButtonIds.ToList().ForEach(btnId =>
        //        {
        //            result.Add((btnId, true));
        //        });  
        //    }
        //    else if (customer.IsAdminUser())
        //    {
        //        var customerRoleIds = customer.GetCustomerRoles()
        //                                      .Where(ar => ar.RoleType == RoleType.BackendUser)
        //                                      .Select(ar => ar.Id);
        //        var moduleMappings = from mp in _systemModuleService.GetSystemModuleCustomerRoleMappingsBySystemModuleId(module.Id)
        //                             where customerRoleIds.Contains(mp.CustomerRoleId) && mp.ViewPermission
        //                             select mp;
        //        if (moduleMappings?.Any(mp => mp.AuthorizedButtons.Count > 0) ?? false)
        //        {
        //            foreach(var btnid in moduleButtonIds)
        //            {
        //                if (moduleMappings.SelectMany(mp => mp.AuthorizedButtons).Contains(btnid))
        //                    result.Add((btnid, true));
        //                else result.Add((btnid, false));
        //            }
        //            //result.ForEach(r =>
        //            //{
        //            //    if (moduleMappings.SelectMany(mp => mp.AuthorizedButtons).Contains(r.btnId))
        //            //        r.canAccess = true;
        //            //});
        //        }
        //    }
        //    return result;
        //}
        public virtual int[] GetModuleAssignedButtonIdsWithAccess(Customer customer, SystemModule module)
        {
            if (customer == null)
                throw new ArgumentNullException(nameof(customer));
            if (module == null)
                throw new ArgumentNullException(nameof(module));
            if (!module.IsActive || module.OpenMode == LinkOpenMode.None) return null;
            if (module.LimitedToStores)
            {
                int[] limitedStoreIds = _storeMappingService.GetStoresIdsWithAccess(module);
                if (!limitedStoreIds?.Contains(_storeContext.CurrentStore.Id) ?? false)
                    return null;
            }
            var moduleButtonIds = module.ModuleButtonIds;
            if (!(moduleButtonIds?.Any() ?? false))
                return null;

            if (customer.IsAdministrator())
                return moduleButtonIds.ToArray();
            else if (customer.IsAdminUser())
            {
                var customerRoleIds = customer.GetCustomerRoles()
                                              .Where(ar => ar.RoleType == RoleType.BackendUser)
                                              .Select(ar => ar.Id);
                var moduleMappings = from mp in _systemModuleService.GetSystemModuleCustomerRoleMappingsBySystemModuleId(module.Id)
                                     where customerRoleIds.Contains(mp.CustomerRoleId) && mp.ViewPermission
                                     select mp;
                if (moduleMappings?.Any(mp => mp.AuthorizedButtons.Count > 0) ?? false)
                    return moduleButtonIds.Intersect(moduleMappings.SelectMany(mp => mp.AuthorizedButtons).Distinct()).ToArray();
            }
            return null;
        }

        /// <summary>
        /// 查询指定模块下的模块按钮是否有操作权限
        /// </summary>
        /// <param name="moduleId"></param>
        /// <param name="buttonId"></param>
        /// <returns></returns>
        public virtual bool AdminModuleButtonAuthorize(int moduleId, int buttonId)
        {
            var moduleEntity = _systemModuleService.GetSystemModuleById(moduleId);
            if (moduleEntity == null)
                throw new NullReferenceException($"Entity cannot be found with moduleId: {moduleId}");
            var btnIdsWithAccess = GetModuleAssignedButtonIdsWithAccess(_workContext.CurrentCustomer, moduleEntity);
            if (btnIdsWithAccess == null)
                return false;
            else if (!btnIdsWithAccess.Contains(buttonId))
                return false;
            else
                return true;
        }
        #endregion
        #endregion

    }
}
